Imports System.Globalization
Imports Aricie.DNN.Caching.Analysis
Imports DotNetNuke.Entities.Modules
Imports Aricie.Collections
Imports Aricie.Services
Imports Aricie.DNN.Services
Imports Aricie.DNN.Diagnostics
Imports Aricie.DNN.Services.Workers
Imports System.Reflection

Namespace Aricie.DNN.Caching
    Public Class DistributedCachingBusinessController
        Implements IPortable

        Public Const MODULE_NAME As String = "Aricie.DistributedCachingProvider"

        Public Shared ReadOnly Property ModuleDefId() As Integer
            Get
                If (_ModuleDefId = -1) Then
                    _ModuleDefId = NukeHelper.GetModuleDefIdByModuleName(MODULE_NAME)
                End If
                Return _ModuleDefId
            End Get
        End Property

#Region "Private members"

        Private Shared _ModuleDefId As Integer = -1
        Private Shared _SettingsFileName As String = ""

        Private Shared _KeyCounter As New Dictionary(Of String, Integer)

        Public Const glbDistributedCachingLogType As String = "DistributedCaching"


        Private Shared _AnalyzeTaskQueue As TaskQueue(Of DateTime)


#End Region

#Region "DNN Interfaces"

        Public Function ExportModule(ByVal moduleID As Integer) As String Implements DotNetNuke.Entities.Modules.IPortable.ExportModule
            Dim pid As Integer = NukeHelper.GetPortalIdByModuleId(moduleID)
            'Dim params As DistributedCachingSettings = Aricie.DNN.Settings.SettingsController.GetModuleSettings(Of DistributedCachingSettings)(DNN.Settings.SettingsScope.HostSettings, -1)
            Dim export As New DistributedCachingExport
            export.Settings = DistributedCachingProviderBase.Settings
            export.Statistics = DistributedCachingBusinessController.LoadStatistics

            'Dim params As DistributedCachingProviderSettings = DistributedCachingProvider.Settings
            Dim doc As System.Xml.XmlDocument = ReflectionHelper.Serialize(export, True)
            Return doc.OuterXml
        End Function

        Public Sub ImportModule(ByVal moduleID As Integer, ByVal content As String, ByVal version As String, ByVal userID As Integer) Implements DotNetNuke.Entities.Modules.IPortable.ImportModule
            Dim export As DistributedCachingExport = ReflectionHelper.Deserialize(Of DistributedCachingExport)(Content)
            'DistributedCachingBusinessController.SaveSettings(export.Settings)
            export.Settings.Save()
            DistributedCachingBusinessController.SaveStatistics(export.Statistics)

        End Sub

#End Region

#Region "Persistence methods"


        Public Shared Function LoadSettings(ByVal useCache As Boolean) As DistributedCachingProviderSettings
            Dim filename As String = GetSettingsFileName()
            Return Aricie.DNN.Settings.SettingsController.LoadFileSettings(Of DistributedCachingProviderSettings)(filename, useCache, True)
        End Function

        'Public Shared Sub SaveSettings(ByVal settings As DistributedCachingProviderSettings)

        '    Dim fileName As String = GetSettingsFileName()
        '    Aricie.DNN.Settings.SettingsController.SaveFileSettings(Of DistributedCachingProviderSettings)(fileName, settings, settings.EnableFastSettingsLoad)
        '    DistributedCachingProviderBase.ClearSettings()

        'End Sub

        Public Shared Function LoadStatistics() As CacheStatistics
            Dim filename As String = GetStatsFileName()
            Return Aricie.DNN.Settings.SettingsController.LoadFileSettings(Of CacheStatistics)(filename, False)
        End Function

        Public Shared Sub SaveStatistics(ByVal stats As CacheStatistics)

            Dim fileName As String = GetStatsFileName()
            Aricie.DNN.Settings.SettingsController.SaveFileSettings(Of CacheStatistics)(fileName, stats)

        End Sub


        Public Shared Function LoadAnalysis() As CacheAnalysis
            Dim filename As String = GetAnalysisFileName()
            Return Aricie.DNN.Settings.SettingsController.LoadFileSettings(Of CacheAnalysis)(filename, False)
        End Function

        Public Shared Sub SaveAnalysis(ByVal obj As CacheAnalysis)

            Dim fileName As String = GetAnalysisFileName()
            Aricie.DNN.Settings.SettingsController.SaveFileSettings(Of CacheAnalysis)(fileName, obj)

        End Sub

#Region "Private methods"

        Private Shared Function GetSettingsFileName() As String
            If _SettingsFileName = "" Then
                _SettingsFileName = System.AppDomain.CurrentDomain.BaseDirectory & "DesktopModules\Aricie.DistributedCachingProvider\Aricie.DistributedCachingProvider.config"
            End If
            Return _SettingsFileName
        End Function

        Private Shared Function GetStatsFileName() As String
            Return DotNetNuke.Common.Globals.HostMapPath & "\" & DistributedCachingProviderBase.Settings.StatisticsSettings.StatisticsFileName
        End Function

        Private Shared Function GetAnalysisFileName() As String
            Return DotNetNuke.Common.Globals.HostMapPath & "\" & DistributedCachingProviderBase.Settings.AutoConfigSettings.AnalysisFileName
        End Function

#End Region

#End Region

#Region "Logs"


        Public Shared Sub LogCachingOperation(ByVal cacheTiming As CacheTimings, ByVal stepStatus As String, ByVal workingPhase As WorkingPhase, ByVal lastStep As Boolean, _
                                           ByVal cacheKey As String, ByVal sucess As Boolean, ByVal ParamArray addProp() As KeyValuePair(Of String, String))

            Dim counterKey As String = cacheKey & cacheTiming.ToString & stepStatus
            Dim count As Integer
            If Not _KeyCounter.TryGetValue(counterKey, count) Then
                count = 0
            End If

            Dim settings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings
            If count < settings.LoggingSettings.MaxNbEventsPerCollector Then

                Dim props As New List(Of KeyValuePair(Of String, String))(addProp.Length + 1)


                If cacheKey <> "" Then

                    props.Add(New KeyValuePair(Of String, String)(CacheStepProperty.CacheKey, cacheKey))
                End If


                'props.Add(New KeyValuePair(Of String, String)(CacheStepProperty.DistributedCachingLogType, logType.ToString))
                props.Add(New KeyValuePair(Of String, String)(CacheStepProperty.CacheTiming, cacheTiming.ToString))
                props.Add(New KeyValuePair(Of String, String)(CacheStepProperty.Success, sucess.ToString(CultureInfo.InvariantCulture)))

                props.AddRange(addProp)


                Dim objStep As StepInfo
                objStep = New StepInfo(glbDistributedCachingLogType, stepStatus, workingPhase, lastStep, False, -1, cacheKey & cacheTiming, props.ToArray)
                PerformanceLogger.Instance.AddDebugInfo(objStep, True)
                _KeyCounter(counterKey) = count + 1
            End If


        End Sub


#End Region

#Region "Statistics"



        Public Shared Function BuildStatsFromLogs(ByVal settings As DistributedCachingProviderSettings, ByVal existing As CacheStatistics) As CacheStatistics
            If settings IsNot Nothing Then
                Dim startComputation As DateTime = Now

                Dim stats As CacheStatistics
                If settings.StatisticsSettings.UpdateExistingStats OrElse existing Is Nothing Then
                    stats = existing
                Else
                    stats = New CacheStatistics()
                End If


                Dim getItemList As List(Of StepInfo) = PerformanceLogger.Instance.GetLogs(-1, glbDistributedCachingLogType, stats.ComputationDate, True, True)

                stats.ComputationDate = startComputation

                stats.NbEventLogs = getItemList.Count


                Dim stepsByCacheKeyAndTiming As Dictionary(Of String, Dictionary(Of CacheTimings, List(Of StepInfo))) = OrganizeSteps(getItemList)

                ComputeIndividualStats(settings.StatisticsSettings, stats, stepsByCacheKeyAndTiming)
                'ComputeDiagnostic(settings.AutoConfigSettings, stats)
                ComputeGlobalStats(stats)
                stats.ComputationDate = DateTime.Now
                If DnnContext.Current.IsAuthenticated Then
                    stats.GeneratedBy = DnnContext.Current.User.Username
                Else
                    stats.GeneratedBy = "Automated Task"
                End If
                stats.ComputationTime.Value = Now.Subtract(startComputation)
                'stats.LastLogTime = getItemList(0).FlowStartTime.Add(getItemList(0).TotalElapsed)
                SaveStatistics(stats)
                Return stats
            End If
            Return Nothing

        End Function

#Region "Private methods"

        Private Shared Function OrganizeSteps(ByVal steps As List(Of StepInfo)) As Dictionary(Of String, Dictionary(Of CacheTimings, List(Of StepInfo)))

            Dim stepsByCacheKeyAndTiming As New Dictionary(Of String, Dictionary(Of CacheTimings, List(Of StepInfo)))
            'Dim xmls As New Dictionary(Of String, Boolean)
            For Each objStep As StepInfo In steps

                Dim cacheKey As String = Nothing
                If objStep.Properties.TryGetValue(CacheStepProperty.CacheKey, cacheKey) _
                    AndAlso Not String.IsNullOrEmpty(cacheKey) Then
                    If objStep.IsLastStep AndAlso objStep.StepNumber > 1 Then
                        If objStep.Properties.ContainsKey(CacheStepProperty.Success) _
                                AndAlso Boolean.Parse(objStep.Properties(CacheStepProperty.Success)) Then
                            Dim cacheTiming As CacheTimings
                            Dim strCacheTiming As String = Nothing
                            If objStep.Properties.TryGetValue(CacheStepProperty.CacheTiming, strCacheTiming) Then
                                cacheTiming = Aricie.Common.GetEnum(Of CacheTimings)(strCacheTiming)
                            Else
                                cacheTiming = CacheTimings.Init
                            End If


                            Dim timingsDico As Dictionary(Of CacheTimings, List(Of StepInfo)) = Nothing
                            If Not stepsByCacheKeyAndTiming.TryGetValue(cacheKey, timingsDico) Then
                                timingsDico = New Dictionary(Of CacheTimings, List(Of StepInfo))
                                stepsByCacheKeyAndTiming(cacheKey) = timingsDico
                            End If
                            Dim innerSteps As List(Of StepInfo) = Nothing
                            If Not timingsDico.TryGetValue(cacheTiming, innerSteps) Then
                                innerSteps = New List(Of StepInfo)
                                timingsDico(cacheTiming) = innerSteps
                            End If
                            'If xmls.ContainsKey(cacheKey) Then
                            '    objStep.Properties(CacheStepProperty.IsXml) = xmls(cacheKey).ToString
                            'End If
                            innerSteps.Add(objStep)
                        End If
                        'ElseIf objStep.Properties.ContainsKey(CacheStepProperty.IsXml) Then
                        '    xmls(cacheKey) = Boolean.Parse(objStep.Properties(CacheStepProperty.IsXml))

                    End If

                End If
            Next
            Return stepsByCacheKeyAndTiming


        End Function

        Private Shared Sub ComputeIndividualStats(ByVal statSettings As StatisticsSettings, ByRef stats As CacheStatistics, _
             ByVal stepsByCacheKeyAndTiming As Dictionary(Of String, Dictionary(Of CacheTimings, List(Of StepInfo))))

            Dim timingOrdered As New SortedList(Of DateTime, String)

            For Each cacheKeyPair As KeyValuePair(Of String, Dictionary(Of CacheTimings, List(Of StepInfo))) In stepsByCacheKeyAndTiming
                Dim objKeyStatistics As CacheKeyStatistics = Nothing
                If Not stats.StatsByKey.TryGetValue(cacheKeyPair.Key, objKeyStatistics) Then
                    objKeyStatistics = New CacheKeyStatistics(cacheKeyPair.Key)

                End If
                Dim seriesDico As New Dictionary(Of CacheTimings, Series(Of STimeSpan))
                Dim serializedDistributionType As CacheDistributionType
                For Each timing As KeyValuePair(Of CacheTimings, List(Of StepInfo)) In cacheKeyPair.Value

                    Dim objSeries As Series(Of STimeSpan) = Nothing
                    'If Not objKeyStatistics.Timings.TryGetValue(timing.Key, objSeries) Then
                    objSeries = New Series(Of STimeSpan)
                    'End If
                    Dim maxVal As TimeSpan = TimeSpan.Zero
                    Dim maxObject As STimeSpan = Nothing
                    For Each objStep As StepInfo In timing.Value
                        Dim toAdd As New STimeSpan(objStep.TotalElapsed)
                        If objStep.TotalElapsed > maxVal Then
                            maxVal = objStep.TotalElapsed
                            maxObject = toAdd
                        End If
                        objSeries.Items.Add(toAdd)
                        If timing.Key = CacheTimings.LocalInsert Then
                            If objStep.Properties.ContainsKey(CacheStepProperty.IsCompressed) Then
                                objKeyStatistics.IsCompressed = Boolean.Parse(objStep.Properties(CacheStepProperty.IsCompressed))
                            End If
                            If objStep.Properties.ContainsKey(CacheStepProperty.DistributionType) Then
                                Dim distrib As CacheDistributionType = Aricie.Common.GetEnum(Of CacheDistributionType)(objStep.Properties(CacheStepProperty.DistributionType))
                                If distrib <> CacheDistributionType.Undefined Then
                                    serializedDistributionType = distrib
                                End If
                            End If
                        Else
                            timingOrdered(objStep.FlowStartTime) = cacheKeyPair.Key
                        End If
                    Next

                    seriesDico(timing.Key) = objSeries
                    'objKeyStatistics.Timings(timing.Key) = objSeries
                Next

                For Each timing As KeyValuePair(Of CacheTimings, Series(Of STimeSpan)) In seriesDico
                    Dim existingSeries As Series(Of STimeSpan) = Nothing
                    Dim targetDest As CacheDistributionType
                    Select Case timing.Key
                        Case CacheTimings.CreateObject
                            targetDest = CacheDistributionType.NotDistributed
                        Case CacheTimings.DistributedCachingGet
                            targetDest = serializedDistributionType
                        Case Else
                            Continue For
                    End Select
                    If Not objKeyStatistics.Timings.TryGetValue(targetDest, existingSeries) Then
                        existingSeries = New Series(Of STimeSpan)
                    End If
                    existingSeries.Items.AddRange(timing.Value.Items)

                    CacheKeyStatistics.StatisticsEngine.ComputeStats(existingSeries)

                    If existingSeries.Mean.Value <> TimeSpan.Zero Then
                        Dim i As Integer = 0
                        While i < existingSeries.Items.Count
                            Dim dev As Integer = Convert.ToInt32(existingSeries.Items(i).Value.Ticks * 100 / existingSeries.Mean.Value.Ticks)
                            If dev < statSettings.MinSampleDeviation OrElse dev > statSettings.MaxSampleDeviation Then
                                existingSeries.Items.RemoveAt(i)
                                CacheKeyStatistics.StatisticsEngine.ComputeStats(existingSeries)
                            Else
                                i += 1
                            End If
                        End While
                    End If

                    objKeyStatistics.Timings(targetDest) = existingSeries
                Next


                Dim minSamples As Integer = Integer.MaxValue
                For Each series As Series(Of STimeSpan) In objKeyStatistics.Timings.Values
                    minSamples = Math.Min(series.Items.Count, minSamples)
                Next
                If minSamples >= statSettings.MinSampleNumber Then
                    stats.StatsByKey(cacheKeyPair.Key) = objKeyStatistics
                End If
            Next
            ComputeCacheSequences(statSettings, stats, timingOrdered)

            For Each keyStat As CacheKeyStatistics In stats.StatsByKey.Values
                ComputeDiagnostic(keyStat)
            Next

        End Sub

        Private Shared Sub ComputeCacheSequences(ByVal statSettings As StatisticsSettings, ByRef stats As CacheStatistics, ByVal timingOrdered As SortedList(Of DateTime, String))

            Dim prevs As New SerializableDictionary(Of String, SerializableDictionary(Of String, Integer))
            Dim nexts As New SerializableDictionary(Of String, SerializableDictionary(Of String, Integer))

            For Each keyPair As KeyValuePair(Of String, CacheKeyStatistics) In stats.StatsByKey
                prevs.Add(keyPair.Key, keyPair.Value.PreviousKeys)
                nexts.Add(keyPair.Key, keyPair.Value.NextKeys)
            Next


            Dim currPrevs As SerializableDictionary(Of String, Integer) = Nothing
            Dim currNexts As SerializableDictionary(Of String, Integer) = Nothing

            Dim currCount As Integer
            Dim prevKey As String
            Dim currkey As String
            Dim nextKey As String
            Dim stepDuration As TimeSpan

            For i As Integer = 1 To timingOrdered.Count - 2
                currkey = timingOrdered.Values(i)
                If stats.StatsByKey.ContainsKey(currkey) Then
                    prevKey = timingOrdered.Values(i - 1)

                    nextKey = timingOrdered.Values(i + 1)
                    If prevKey <> currkey Then
                        stepDuration = timingOrdered.Keys(i) - timingOrdered.Keys(i - 1)
                        If stepDuration < statSettings.MaxSequenceDuration.Value Then
                            If Not prevs.TryGetValue(currkey, currPrevs) Then
                                currPrevs = New SerializableDictionary(Of String, Integer)
                            End If
                            If Not currPrevs.TryGetValue(prevKey, currCount) Then
                                currCount = 0
                            End If
                            currPrevs(prevKey) = currCount + 1
                            prevs(currkey) = currPrevs
                        End If

                    End If
                    If nextKey <> currkey Then
                        stepDuration = timingOrdered.Keys(i + 1) - timingOrdered.Keys(i)
                        If stepDuration < statSettings.MaxSequenceDuration.Value Then
                            If Not nexts.TryGetValue(currkey, currNexts) Then
                                currNexts = New SerializableDictionary(Of String, Integer)
                            End If
                            If Not currNexts.TryGetValue(nextKey, currCount) Then
                                currCount = 0
                            End If
                            currNexts(nextKey) = currCount + 1
                            nexts(currkey) = currNexts
                        End If
                    End If
                End If
            Next

            Dim sortedValues As List(Of Integer)
            Dim keysToDelete As New Dictionary(Of String, List(Of String))
            Dim min As Integer
            For Each keyPair As KeyValuePair(Of String, SerializableDictionary(Of String, Integer)) In prevs
                If keyPair.Value.Count > 0 Then
                    '    Dim mockException As New ApplicationException(String.Format("empty sequence for key {0}", keyPair.Key))
                    '    DotNetNuke.Services.Exceptions.Exceptions.LogException(mockException)
                    'Else

                    Dim tempKeysToDelete As New List(Of String)
                    sortedValues = New List(Of Integer)(keyPair.Value.Values)
                    sortedValues.Sort()
                    min = Convert.ToInt32(sortedValues(sortedValues.Count - 1) * statSettings.MinSequenceRatioForGraph / 100)
                    For Each seqkey As KeyValuePair(Of String, Integer) In keyPair.Value
                        If seqkey.Value < min Then
                            tempKeysToDelete.Add(seqkey.Key)
                        End If
                    Next
                    keysToDelete(keyPair.Key) = tempKeysToDelete
                End If

            Next
            For Each keysToDeleteByKey As KeyValuePair(Of String, List(Of String)) In keysToDelete
                Dim seq As Dictionary(Of String, Integer) = prevs(keysToDeleteByKey.Key)
                For Each toDelete As String In keysToDeleteByKey.Value
                    seq.Remove(toDelete)
                Next
            Next
            For Each key As String In prevs.Keys
                stats.StatsByKey(key).PreviousKeys = prevs(key)
            Next

            keysToDelete.Clear()
            For Each keyPair As KeyValuePair(Of String, SerializableDictionary(Of String, Integer)) In nexts
                If keyPair.Value.Count > 0 Then
                    '    Dim mockException As New ApplicationException(String.Format("empty sequence for key {0}", keyPair.Key))
                    '    DotNetNuke.Services.Exceptions.Exceptions.LogException(mockException)
                    'Else
                    Dim tempKeysToDelete As New List(Of String)
                    sortedValues = New List(Of Integer)(keyPair.Value.Values)
                    sortedValues.Sort()
                    min = Convert.ToInt32(sortedValues(sortedValues.Count - 1) * statSettings.MinSequenceRatioForGraph / 100)
                    For Each seqkey As KeyValuePair(Of String, Integer) In keyPair.Value
                        If seqkey.Value < min Then
                            tempKeysToDelete.Add(seqkey.Key)
                        End If
                    Next
                    keysToDelete(keyPair.Key) = tempKeysToDelete
                End If
            Next
            For Each keysToDeleteByKey As KeyValuePair(Of String, List(Of String)) In keysToDelete
                Dim seq As Dictionary(Of String, Integer) = nexts(keysToDeleteByKey.Key)
                For Each toDelete As String In keysToDeleteByKey.Value
                    seq.Remove(toDelete)
                Next
            Next
            For Each key As String In nexts.Keys
                stats.StatsByKey(key).NextKeys = nexts(key)
            Next

        End Sub

        Private Shared Sub ComputeDiagnostic(ByRef keyStat As CacheKeyStatistics)
            keyStat.Diagnostic = New CacheDiagnostic
            Dim objDistribs As New List(Of KeyDistributionStatistic)

            For Each distrib As KeyValuePair(Of CacheDistributionType, Series(Of STimeSpan)) In keyStat.Timings
                Dim tempDuration As TimeSpan = distrib.Value.Mean.Value
                objDistribs.Add(New KeyDistributionStatistic(distrib.Key, tempDuration))
            Next

            If objDistribs.Count > 0 Then
                objDistribs.Sort(Comparer(Of KeyDistributionStatistic).Default)
                objDistribs(0).SetRatio(100)
                Dim bestTicks As Long = objDistribs(0).MeanTime.Value.Ticks
                If bestTicks <> 0 Then
                    For i As Integer = 1 To objDistribs.Count - 1
                        objDistribs(i).SetRatio(Convert.ToInt32((100 * objDistribs(i).MeanTime.Value.Ticks) / bestTicks))
                    Next
                End If
            End If

            keyStat.Diagnostic.SortedDistributionTypes = objDistribs

            If keyStat.PreviousKeys.Count > 0 Then
                Dim totalKey As Integer = 0
                Dim bestPair As New KeyValuePair(Of String, Integer)("", 0)
                For Each keyPair As KeyValuePair(Of String, Integer) In keyStat.PreviousKeys
                    If keyPair.Value > bestPair.Value Then
                        bestPair = keyPair
                    End If
                    totalKey += keyPair.Value
                Next
                keyStat.Diagnostic.UsualPrevKey = New KeySequenceStat(bestPair.Key, bestPair.Value, totalKey)
            End If

            If keyStat.NextKeys.Count > 0 Then
                Dim totalKey As Integer = 0
                Dim bestPair As New KeyValuePair(Of String, Integer)("", 0)
                For Each keyPair As KeyValuePair(Of String, Integer) In keyStat.NextKeys
                    If keyPair.Value > bestPair.Value Then
                        bestPair = keyPair
                    End If
                    totalKey += keyPair.Value
                Next
                keyStat.Diagnostic.UsualNextKey = New KeySequenceStat(bestPair.Key, bestPair.Value, totalKey)
            End If

        End Sub


        Private Shared Sub ComputeGlobalStats(ByRef stats As CacheStatistics)

            stats.AverageKeyStatistics = New CacheKeyStatistics()

            stats.AverageKeyStatistics.Timings(CacheDistributionType.Undefined) = New Series(Of STimeSpan)
            For Each cacheKeyPair As KeyValuePair(Of String, CacheKeyStatistics) In stats.StatsByKey
                If cacheKeyPair.Value.Diagnostic.SortedDistributionTypes.Count > 1 Then
                    For Each timing As KeyValuePair(Of CacheDistributionType, Series(Of STimeSpan)) In cacheKeyPair.Value.Timings
                        Dim objSeries As Series(Of STimeSpan) = Nothing
                        If Not stats.AverageKeyStatistics.Timings.TryGetValue(timing.Key, objSeries) Then
                            objSeries = New Series(Of STimeSpan)
                        End If
                        objSeries.Items.Add(timing.Value.Mean)
                        stats.AverageKeyStatistics.Timings(timing.Key) = objSeries
                    Next
                    stats.AverageKeyStatistics.Timings(CacheDistributionType.Undefined).Items.Add(cacheKeyPair.Value.Diagnostic.SortedDistributionTypes(0).MeanTime)
                End If
            Next

            Dim timings As New List(Of CacheDistributionType)(stats.AverageKeyStatistics.Timings.Keys)
            For Each timing As CacheDistributionType In timings
                CacheKeyStatistics.StatisticsEngine.ComputeStats(stats.AverageKeyStatistics.Timings(timing))
            Next

            ComputeDiagnostic(stats.AverageKeyStatistics)
        End Sub

#End Region


#End Region

#Region "Auto Configure"
        Private Shared _StartTime As DateTime = Now

        Private Shared _LastAnalysis As DateTime = _StartTime
        Private Shared _LastStats As DateTime = _StartTime
        Private Shared _LastClearLocal As DateTime = _StartTime
        Private Shared _LastClearRegion As DateTime = _StartTime
        Private Shared _LastChangeDefaultSerializer As DateTime = _StartTime
        Private Shared _LastSwitchIconvertible As DateTime = _StartTime

        Public Shared Sub ClearAnalyzeQueue()
            If _AnalyzeTaskQueue IsNot Nothing Then
                _AnalyzeTaskQueue.Terminate()
                _AnalyzeTaskQueue = Nothing
            End If
        End Sub

        Public Shared ReadOnly Property AnalyzeTaskQueue() As TaskQueue(Of DateTime)
            Get
                If _AnalyzeTaskQueue Is Nothing Then
                    Dim autoConfigSettings As AutoConfigSettings = DistributedCachingProviderBase.Settings.AutoConfigSettings
                    _AnalyzeTaskQueue = New TaskQueue(Of DateTime)(New System.Action(Of DateTime)(AddressOf AutoAnalyze), 1, True, _
                                                                   TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15), autoConfigSettings.ClearLocalCachePeriod.Value)
                End If
                Return _AnalyzeTaskQueue
            End Get
        End Property


        Public Shared Sub AutoAnalyze(ByVal objTime As DateTime)
            Dim objAutoConfigSettings As AutoConfigSettings = DistributedCachingProviderBase.Settings.AutoConfigSettings
            objTime = Now

           
           
            Try
                If objTime.Subtract(_LastStats) > objAutoConfigSettings.GenerateStatisticsPeriod.Value Then
                    _LastStats = Now
                    BuildStatsFromLogs(DistributedCachingProviderBase.Settings, DistributedCachingBusinessController.LoadStatistics())
                End If
                If objTime.Subtract(_LastSwitchIconvertible) > objAutoConfigSettings.SwitchIConvertiblePeriod.Value Then
                    _LastSwitchIconvertible = Now
                    Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings
                    objSettings.CloudSettings.SkipIConvertible = Not objSettings.CloudSettings.SkipIConvertible
                    'DistributedCachingBusinessController.SaveSettings(objSettings)
                    objSettings.Save()
                End If
                If objTime.Subtract(_LastChangeDefaultSerializer) > objAutoConfigSettings.ChangeDefaultSerializer.Value Then
                    _LastChangeDefaultSerializer = Now
                    Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings
                    Select Case objSettings.CloudSettings.DefaultSerializationMode
                        Case CacheDistributionType.Serialized
                            If objAutoConfigSettings.SkipBinarySerializer Then
                                objSettings.CloudSettings.DefaultSerializationMode = CacheDistributionType.XmlSerialized
                            Else
                                objSettings.CloudSettings.DefaultSerializationMode = CacheDistributionType.NativeBinary
                            End If
                        Case CacheDistributionType.NativeBinary
                            objSettings.CloudSettings.DefaultSerializationMode = CacheDistributionType.XmlSerialized
                        Case CacheDistributionType.XmlSerialized
                            objSettings.CloudSettings.DefaultSerializationMode = CacheDistributionType.JsonNet
                        Case Else
                            objSettings.CloudSettings.DefaultSerializationMode = CacheDistributionType.Serialized
                    End Select
                    'DistributedCachingBusinessController.SaveSettings(objSettings)
                    objSettings.Save()
                End If
                If objTime.Subtract(_LastAnalysis) > objAutoConfigSettings.AnalysisPeriod.Value Then
                    _LastAnalysis = Now
                    PerformAnalysisFromStatistics(DistributedCachingBusinessController.LoadStatistics(), DistributedCachingProviderBase.Settings)
                End If
            Catch ex As Exception
                DotNetNuke.Services.Exceptions.LogException(ex)
            End Try

            If objTime.Subtract(_LastClearRegion) > objAutoConfigSettings.ClearRegionsPeriod.Value Then
                _LastClearRegion = Now
                DistributedCachingProviderBase.ClearRegions()
            End If

            If objTime.Subtract(_LastClearLocal) > objAutoConfigSettings.ClearLocalCachePeriod.Value Then
                _LastClearLocal = Now
                DistributedCachingProviderBase.ClearCache(False)
            End If

            AnalyzeTaskQueue.EnqueueTask(Now)

        End Sub


        Public Shared Function PerformAnalysisFromStatistics(ByVal stats As CacheStatistics, ByVal targetSettings As DistributedCachingProviderSettings) As CacheAnalysis

            Dim filteredStats As CacheStatistics = FilterStats(stats, targetSettings.AutoConfigSettings)

            Dim existingPool As New TargetStrategyPool(targetSettings.CloudSettings)
            Dim targetPool As New TargetStrategyPool(filteredStats)


            Dim toReturn As CacheAnalysis = targetPool.Compute(targetSettings.AutoConfigSettings, existingPool)

            SaveAnalysis(toReturn)

            Return toReturn

        End Function

        Public Shared Sub ApplyAnalysisRecommandations(ByVal analysis As CacheAnalysis, ByVal targetSettings As DistributedCachingProviderSettings)

            'If Not targetSettings.AutoConfigSettings.OverrideStatistics Then
            '    MergeStats(analysis, targetSettings.CloudSettings)
            'End If

            UpdateSettings(targetSettings, analysis)

            'SaveSettings(targetSettings)
            targetSettings.Save()

        End Sub


#Region "Private methods"

        'Private Shared Sub MergeStats(ByRef analysis As CacheAnalysis, ByVal CloudSettings As CloudSettings)
        '    For Each keyPair As KeyValuePair(Of String, CacheStrategy) In CloudSettings.IndividualStrategies
        '        Dim newStat As CacheKeyStatistics = Nothing
        '        If analysis.Statistics.StatsByKey.TryGetValue(keyPair.Key, newStat) Then
        '            newStat.Merge(keyPair.Value.Statistics)
        '            ComputeDiagnostic(newStat)
        '        End If
        '    Next
        '    For Each objGroup As CacheGroupInfo In CloudSettings.GroupStrategies
        '        For Each mergeGroup As CacheGroupInfo In analysis.GroupStrategies
        '            If mergeGroup.Name = objGroup.Name Then
        '                mergeGroup.Statistics.Merge(objGroup.Statistics)
        '                ComputeDiagnostic(mergeGroup.Statistics)
        '            End If
        '        Next
        '    Next
        'End Sub

        Private Shared Function FilterStats(ByRef stats As CacheStatistics, ByVal autoConfigSettings As AutoConfigSettings) As CacheStatistics
            Dim toReturn As New CacheStatistics
            Dim validSeries As Boolean
            For Each keyPair As KeyValuePair(Of String, CacheKeyStatistics) In stats.StatsByKey
                validSeries = True
                For Each objTiming As KeyValuePair(Of CacheDistributionType, Series(Of STimeSpan)) In keyPair.Value.Timings
                    If objTiming.Value.CoefOfVariation > autoConfigSettings.MaxCoefOfVariation Then
                        validSeries = False
                        Exit For
                    End If
                Next
                If validSeries Then
                    toReturn.StatsByKey(keyPair.Key) = keyPair.Value
                End If
            Next
            Return toReturn
        End Function


        Private Shared Sub UpdateSettings(ByRef targetSettings As DistributedCachingProviderSettings, ByVal analysis As CacheAnalysis)

            If Not targetSettings.AutoConfigSettings.OverrideKeysOnAutoConfig Then
                targetSettings.CloudSettings.GroupStrategies = analysis.GroupStrategies
            Else
                targetSettings.CloudSettings.GroupStrategies.AddRange(analysis.GroupStrategies)
            End If


        End Sub

        'Private Shared Function ComputeStrategyFromKeyStats(ByVal stat As CacheKeyStatistics) As CacheKeyInfo
        '    Dim toReturn As New CacheKeyInfo()

        '    toReturn.DistributionType = GetDistributionType(stat, DistributedCachingProvider.Settings.AutoConfigSettings.MaxCoefOfVariation)


        '    Return toReturn
        'End Function

        'Private Shared Function GetDistributionType(ByVal stat As CacheKeyStatistics, ByVal maxRelativeStandardError As Integer) As CacheDistributionType
        '    Dim toReturn As CacheDistributionType = CacheDistributionType.Undefined
        '    Dim createTime As Series(Of STimeSpan) = Nothing
        '    stat.Timings.TryGetValue(CacheTimings.InsertObject, createTime)
        '    If createTime IsNot Nothing _
        '        AndAlso createTime.CoefOfVariation < DistributedCachingProvider.Settings.AutoConfigSettings.MaxCoefOfVariation Then
        '        Dim serializedVGetTime As Series(Of STimeSpan) = Nothing
        '        Dim xmlSerializedVGetTime As Series(Of STimeSpan) = Nothing
        '        stat.Timings.TryGetValue(CacheTimings.DistributedCachingGet, serializedVGetTime)
        '        stat.Timings.TryGetValue(CacheTimings.DistributedCachingGet, xmlSerializedVGetTime)
        '        If serializedVGetTime Is Nothing _
        '            OrElse serializedVGetTime.CoefOfVariation > DistributedCachingProvider.Settings.AutoConfigSettings.MaxCoefOfVariation _
        '            OrElse serializedVGetTime.Mean.Value > createTime.Mean.Value Then
        '            toReturn = CacheDistributionType.NotDistributed
        '        Else
        '            toReturn = CacheDistributionType.Serialized
        '        End If
        '        If xmlSerializedVGetTime IsNot Nothing _
        '            AndAlso xmlSerializedVGetTime.CoefOfVariation < DistributedCachingProvider.Settings.AutoConfigSettings.MaxCoefOfVariation Then
        '            If (toReturn = CacheDistributionType.NotDistributed AndAlso xmlSerializedVGetTime.Mean.Value < createTime.Mean.Value) _
        '                OrElse (toReturn = CacheDistributionType.Serialized AndAlso xmlSerializedVGetTime.Mean.Value < serializedVGetTime.Mean.Value) Then
        '                toReturn = CacheDistributionType.XmlSerialized
        '            End If
        '        End If

        '    End If
        '    Return toReturn
        'End Function


        'Private Shared Sub ComputeDiagnostic(ByVal settings As AutoConfigSettings, ByRef stats As CacheStatistics)

        'End Sub

#End Region

#End Region

    End Class
End Namespace